﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;

namespace ClassifierLib
{
    /// <summary>
    /// Provides a training set for the multiplexer problem
    /// </summary>
    public class MultiplexerTrainingSet : TrainingSet
    {
        private int _count;
        private int _vectorLength;
        private int _tempTrainingSetCount;   //DEBUG - BPH

        /// <summary>
        /// Creates a random training set of 1000 values
        /// </summary>
        public MultiplexerTrainingSet()
        {
            _count = 1000; // default training set size
            _vectorLength = 6; // default vector length
            ActionFactory = new MultiplexerActionFactory();

            _tempTrainingSetCount = 0;  //DEBUG - BPH
        }

        /// <summary>
        /// Creates a random training set of specified size
        /// </summary>
        /// <param name="Count">Number of training values</param>
        /// <param name="Length">Length of training value vector</param>
        public MultiplexerTrainingSet(int Count, int Length)
        {
            this.Count = Count;
            this.VectorLength = Length;
            ActionFactory = new MultiplexerActionFactory();
        }

        

        public override TrainingValue GetNext()
        {
            Count--;

            Debug.Assert(Count >= 0,"GetNext called on trainingset after finised flag set");

            // create random state vector
            List<Double> values = new List<double>();

            double action = 0;

            //Uncomment block of code below if wanting to use a consistent training set for each run of 6-MUX
            /*            
            //List<double> values1 = new List<double> { 0, 0, 0, 0, 0, 0 };
            //List<double> values2 = new List<double> { 0, 0, 1, 0, 0, 0 };
            //List<double> values3 = new List<double> { 0, 1, 0, 1, 0, 0 };
            //List<double> values4 = new List<double> { 0, 1, 0, 0, 0, 0 };
            //List<double> values5 = new List<double> { 1, 0, 0, 0, 1, 0 };
            //List<double> values6 = new List<double> { 1, 0, 0, 0, 0, 0 };
            //List<double> values7 = new List<double> { 1, 1, 0, 0, 0, 1 };
            //List<double> values8 = new List<double> { 1, 1, 0, 0, 0, 0 };
            //List<double> values9 = new List<double> { 0, 0, 0, 1, 0, 0 };
            //List<double> values10 = new List<double> { 0, 0, 0, 0, 1, 0 };
            List<double> values1 = new List<double> { 0, 0, 0, 0, 0, 0 };//0
            List<double> values2 = new List<double> { 0, 1, 0, 1, 0, 0 };//1
            List<double> values3 = new List<double> { 0, 0, 0, 1, 1, 0 };//0
            List<double> values4 = new List<double> { 0, 0, 0, 1, 1, 1 };//0
            List<double> values5 = new List<double> { 0, 0, 0, 0, 1, 1 };//0
            List<double> values6 = new List<double> { 0, 0, 0, 0, 0, 1 };//0
            List<double> values7 = new List<double> { 0, 0, 0, 1, 0, 1 };//0
            List<double> values8 = new List<double> { 0, 0, 1, 0, 0, 0 };//1
            List<double> values9 = new List<double> { 0, 1, 0, 1, 0, 0 };//1
            List<double> values10 = new List<double> { 0, 1, 0, 1, 1, 0 };//1
            List<double> valuesDefault = new List<double> { 1, 1, 1, 1, 1, 1 };//1

            if (_tempTrainingSetCount >= 10)
                _tempTrainingSetCount = 0;

            switch (_tempTrainingSetCount)
            {
                case 0:
                    values = values1;
                    break;
                case 1:
                    values = values2;
                    break;
                case 2:
                    values = values3;
                    break;
                case 3:
                    values = values4;
                    break;
                case 4:
                    values = values5;
                    break;
                case 5:
                    values = values6;
                    break;
                case 6:
                    values = values7;
                    break;
                case 7:
                    values = values8;
                    break;
                case 8:
                    values = values9;
                    break;
                case 9:
                    values = values10;
                    break;
                default:
                    values = valuesDefault;
                    break;
            }
            _tempTrainingSetCount++;
            */

            //DEBUG - BPH
            values.Clear();

            for (int i = 0; i < _vectorLength; i++)
            {
                values.Add(PersistantRandom.Instance.Next(2));
            }

            

            // Will: the following code will calculate a multiplexer of any length, but for now have hard-coded to the approach you asked for
            //int multiplier = 1;
            //double total = 0; 

            //for (int i = 0; i < BitCount; i++)
            //{
            //    total = values[i] * multiplier;
            //    multiplier *= 2;
            //}

            //double action = total;

            // Will: this is not safe for low vector lengths, can this be refactored to a solution where the number of bits are a function of vector length, like above?
            
            //6 bit MUX
            if (VectorLength == 6)
            {
                int n = (int)(2 * values[0] + values[1]) + 2;
                action = values[n];
            }

            //11 bit MUX
            if (VectorLength == 11)
            {
                int n = (int)(4 * values[0] + 2 * values[1] + values[2]) + 3;
                action = values[n];
            }

            //20 bit MUX
            if (VectorLength == 20)
            {
                int n = (int)(8 * values[0] + 4 * values[1] + 2 * values[2] + values[3]) + 4;
                action = values[n];
            }

            return new TrainingValue(new Environment.State(values), new Action(action));
        }


        public override bool IsFinished
        {
            get
            {
                return (Count<=0);
            }
        }

        /// <summary>
        /// Sets the number of random values to generate before completion
        /// </summary>
        [CategoryAttribute("Settings"), Browsable(true), ReadOnly(false), BindableAttribute(false), DesignOnly(false), DescriptionAttribute("Number of items in training set")]
        public int Count
        {
            get
            {
                return _count;
            }
            set
            {
                _count = value;
                OnPropertyChanged("Count");

                if (_count < 0)
                    OnPropertyChanged("IsFinished");
            }
        }


        [CategoryAttribute("Settings"), Browsable(true), ReadOnly(false), BindableAttribute(false), DesignOnly(false), DescriptionAttribute("length in bits of vector returned by training set")]
        public int VectorLength
        {
            get
            {
                return _vectorLength;
            }
            set
            {
                _vectorLength = value;
                UpdateBitCount();
                OnPropertyChanged("VectorLength");
            }
        }

        private int BitCount;

        private void UpdateBitCount()
        {
            // calculate the correct max number of bits in multiplexer based on vectorlength
            int BitCount = 1;
            while (Math.Pow(2.0, BitCount) < _vectorLength)
            {
                BitCount++;
            }
        }

    }
}